/*
 * Copyright (c) 2025 Li Auto Inc. and its affiliates
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "LixdpSocketUDP.hpp"
#include "UDPChannelResource.h"
#include <transport/ReceiverResource.h>

#include <elog/Log.hpp>

static int log_cb(int log_level, char* buf, int size, void*) {

    if (!buf || strlen(buf) != static_cast<size_t>(size))
        return -1;

    switch (log_level) {
        case XUDP_LOG_ERR:
            logError(LIXDP, buf);
            break;
        case XUDP_LOG_WARN:
            logWarning(LIXDP, buf);
            break;
        case XUDP_LOG_INFO:
            logInfo(LIXDP, buf);
            break;
        case XUDP_LOG_DEBUG:
        default:
            logDebug(LIXDP, buf);
            break;
    }

    return 0;
}

namespace vbs {
namespace transport {

using IPLocator = vbsutil::xmlparser::IPLocator;
using Locator_t = vbsutil::xmlparser::Locator_t;

LixdpSocketUDP::LixdpSocketUDP(std::string& ip, uint16_t port, uint32_t xdp_rx_queue_num)
    : LixdpSocket(ip, port, xdp_rx_queue_num), channel_resource_(nullptr) {
    (void)IPLocator::setIPv4(input_locator_, ip);
    (void)IPLocator::setPhysicalPort(input_locator_, port);
    set_log_cb(log_cb);
}

bool LixdpSocketUDP::sockaddr_to_locator(const struct sockaddr* remote_addr, vbsutil::xmlparser::Locator_t& locator) {
    if (remote_addr && remote_addr->sa_family == AF_INET) {
        locator.kind = LOCATOR_KIND_UDPv4;
        (void)IPLocator::setIPv4(locator, ntohl(((sockaddr_in*)remote_addr)->sin_addr.s_addr));
        (void)IPLocator::setPhysicalPort(locator, ntohs(((sockaddr_in*)remote_addr)->sin_port));
    } else {
        return false;
    }
    return true;
}

bool LixdpSocketUDP::locator_to_sockaddr(const vbsutil::xmlparser::Locator_t& locator, struct sockaddr* remote_addr) {
    if (remote_addr && locator.kind == LOCATOR_KIND_UDPv4) {
        sockaddr_in* addr = (sockaddr_in*)remote_addr;
        addr->sin_family = AF_INET;
        if (inet_aton(IPLocator::toIPv4string(locator).c_str(), &addr->sin_addr) == 0) {
            return false;
        }
        addr->sin_port = htons(IPLocator::getPhysicalPort(locator));
    } else {
        return false;
    }
    return true;
}

int LixdpSocketUDP::lixdp_receive(const uint8_t* buffer, size_t length, struct sockaddr* remote_addr) {

    logDebug(RTPS_MSG_IN, "Lixdp receive length: " << length);
    if (channel_resource_->alive()) {
        if (length > 0) {
            // This is not necessary anymore but it's left here for back compatibility with versions
            // older than 1.8.1
            if ((length == 13) && (memcmp(buffer, "EPRORTPSCLOSE", 13) == 0)) {
                return 0;
            }
        }
        // Blocking receive.
        // Processes the data through the CDR Message interface.
        vbsutil::xmlparser::Locator_t remote_locator;
        if (sockaddr_to_locator(remote_addr, remote_locator)) {
            if (channel_resource_->message_receiver() != nullptr) {
                channel_resource_->message_receiver()->OnDataReceived(buffer, length, input_locator_, remote_locator);
            } else if (channel_resource_->alive()) {
                logInfo(RTPS_MSG_IN, "Received Message, but no receiver attached");
            }
        }
    } else {
        logInfo(RTPS_MSG_IN, "UDPChannelResource is not alive");
        // 关闭lixdp接收
        set_alive(false);
        channel_resource_->message_receiver(nullptr);
        // 关闭接收
        return 0;
    }

    // channel_resource_->message_receiver(nullptr);
    return 0;
}

int LixdpSocketUDP::load_kern_prog(std::vector<std::string>& dev_names) {
    return LixdpSocket::load_kern_prog(log_cb, dev_names);
}
}  // namespace transport
}  // namespace vbs
